package com.blogspot.mkorwel.jaas;

import java.util.Map;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

import net.sf.json.JSONException;
import net.sf.json.JSONObject;

import com.fourspaces.couchdb.Database;
import com.fourspaces.couchdb.Session;
import com.fourspaces.couchdb.View;

/**
 * 
 * @author mkorwel
 * 
 */
public class CouchDBLoginModule implements LoginModule {

	private Session s;

	private Database db;
	private JSONObject userDoc;
	// initial state
	private Subject subject;
	private CallbackHandler callbackHandler;
	@SuppressWarnings("unused")
	private Map<String, ?> sharedState;
	private Map<String, ?> options;

	// username and password
	private String username;
	private String password;

	private CouchDBPrincipal userPrincipal;
	private CouchDBGroup userGroup;

	public void initialize(Subject subject, CallbackHandler callbackHandler,
			Map<String, ?> sharedState, Map<String, ?> options) {

		this.subject = subject;
		this.callbackHandler = callbackHandler;
		this.sharedState = sharedState;
		this.options = options;

	}

	public boolean login() throws LoginException {
		s = new Session((String)options.get("host"), 5984);
		db = s.getDatabase((String)options.get("database"));

		Callback[] callbacks = new Callback[2];
		callbacks[0] = new NameCallback("user name: ");
		callbacks[1] = new PasswordCallback("password: ", false);

		try {
			// pobranie loginu
			callbackHandler.handle(callbacks);

			username = ((NameCallback) callbacks[0]).getName();

			// pobranie hasla
			char[] tmpPassword = ((PasswordCallback) callbacks[1])
					.getPassword();
			if (tmpPassword == null) {
				tmpPassword = new char[0];
			}
			password = new String(tmpPassword);

			((PasswordCallback) callbacks[1]).clearPassword();

		} catch (java.io.IOException ioe) {
			throw new LoginException(ioe.toString());
		} catch (UnsupportedCallbackException uce) {
			throw new LoginException("Error: " + uce.getCallback().toString()
					+ " not available to garner authentication information "
					+ "from the user");
		}

		// weryfikacje loginu i hasla
		try {
			
			View vi = new View((String)options.get("view"));
			vi.setKeyString(username);
			
			userDoc = db.view(vi).getResults().get(0).getJSONObject("value");

		} catch (JSONException e) {
			userDoc = null;
		}

		return userDoc != null
				&& userDoc.getString((String)options.get("password-field")).equals(password);
	}

	public boolean commit() throws LoginException {

		userPrincipal = new CouchDBPrincipal(username);
		userGroup = new CouchDBGroup(userDoc.getString((String)options.get("role-field")));

		if (!subject.getPrincipals().contains(userPrincipal)) {
			subject.getPrincipals().add(userPrincipal);
			subject.getPrincipals().add(userGroup);
		}

		return true;
	}

	public boolean abort() throws LoginException {

		username = null;
		password = null;
		userPrincipal = null;
		userGroup = null;
		userDoc = null;

		return true;
	}

	public boolean logout() throws LoginException {

		subject.getPrincipals().remove(userPrincipal);

		username = null;
		password = null;
		userPrincipal = null;
		userGroup = null;
		userDoc = null;

		return true;
	}

}